using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Atlantic.XpoAutomaticProperties
{
   internal static class AopUtils
   {
      #region Static Methods

      /// <summary>
      /// Gets a property's backing field.
      /// </summary>
      /// <param name="targetType">Type of the target.</param>
      /// <param name="property">The property.</param>
      /// <returns></returns>
      public static FieldInfo GetBackingField(Type targetType, PropertyInfo property)
      {
         string backingFieldName = null;
         foreach (var attribute in GetAttributes<PropertyBackingFieldAttribute>(property))
         {
            backingFieldName = attribute.Name;
            break; // Taking the first one we find!
         }

         if (string.IsNullOrEmpty(backingFieldName))
         {
            backingFieldName = GetAutoPropertyBackingFieldName(property);
         }
         return targetType.UnderlyingSystemType.GetField(backingFieldName,
                                                         BindingFlags.Instance | BindingFlags.Public |
                                                         BindingFlags.NonPublic);
      }

      internal static bool IsIgnored(MemberInfo member)
      {
         return GetAttributes<XpoIgnoreAutomaticPropertyAttribute>(member).FirstOrDefault() != null;
      }

      internal static IEnumerable<T> GetAttributes<T>(MemberInfo member) where T : Attribute
      {
         var attributes = Attribute.GetCustomAttributes(member, typeof (T));
         if (attributes != null)
         {
            foreach (var attribute in attributes.OfType<T>())
            {
               yield return attribute;
            }
         }
      }

      internal static string GetAutoPropertyBackingFieldName(PropertyInfo property)
      {
         // Compiler scheme for auto properties = <propertyname>k__BackingField
         return String.Concat("<", property.Name, ">k__BackingField");
      }

      /// <summary>
      /// Reports the specified values.
      /// </summary>
      /// <param name="values">The values.</param>
      internal static void Report(params object[] values)
      {
         var builder = new StringBuilder();
         foreach (var value in values)
         {
            builder.Append(value);
            builder.Append(" ");
         }
         Console.WriteLine(builder);
      }

      #endregion
   }
}